{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "\n",
    "import math\n",
    "\n",
    "from IPython import display\n",
    "from matplotlib import cm\n",
    "from matplotlib import gridspec\n",
    "from matplotlib import pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from sklearn import metrics\n",
    "import tensorflow as tf\n",
    "from tensorflow.python.data import Dataset\n",
    "\n",
    "tf.logging.set_verbosity(tf.logging.ERROR)\n",
    "pd.options.display.max_rows = 10\n",
    "pd.options.display.float_format = '{:.1f}'.format\n",
    "\n",
    "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n",
    "\n",
    "california_housing_dataframe = california_housing_dataframe.reindex(\n",
    "    np.random.permutation(california_housing_dataframe.index))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def preprocess_features(california_housing_dataframe):\n",
    "  \"\"\"Prepares input features from California housing data set.\n",
    "\n",
    "  Args:\n",
    "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
    "      from the California housing data set.\n",
    "  Returns:\n",
    "    A DataFrame that contains the features to be used for the model, including\n",
    "    synthetic features.\n",
    "  \"\"\"\n",
    "  selected_features = california_housing_dataframe[\n",
    "    [\"latitude\",\n",
    "     \"longitude\",\n",
    "     \"housing_median_age\",\n",
    "     \"total_rooms\",\n",
    "     \"total_bedrooms\",\n",
    "     \"population\",\n",
    "     \"households\",\n",
    "     \"median_income\"]]\n",
    "  processed_features = selected_features.copy()\n",
    "  # Create a synthetic feature.\n",
    "  processed_features[\"rooms_per_person\"] = (\n",
    "    california_housing_dataframe[\"total_rooms\"] /\n",
    "    california_housing_dataframe[\"population\"])\n",
    "  return processed_features\n",
    "\n",
    "def preprocess_targets(california_housing_dataframe):\n",
    "  \"\"\"Prepares target features (i.e., labels) from California housing data set.\n",
    "\n",
    "  Args:\n",
    "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
    "      from the California housing data set.\n",
    "  Returns:\n",
    "    A DataFrame that contains the target feature.\n",
    "  \"\"\"\n",
    "  output_targets = pd.DataFrame()\n",
    "  # Scale the target to be in units of thousands of dollars.\n",
    "  output_targets[\"median_house_value\"] = (\n",
    "    california_housing_dataframe[\"median_house_value\"] / 1000.0)\n",
    "  return output_targets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training examples summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>latitude</th>\n",
       "      <th>longitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>rooms_per_person</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>35.6</td>\n",
       "      <td>-119.6</td>\n",
       "      <td>28.6</td>\n",
       "      <td>2634.1</td>\n",
       "      <td>537.9</td>\n",
       "      <td>1422.1</td>\n",
       "      <td>499.5</td>\n",
       "      <td>3.9</td>\n",
       "      <td>2.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>2.1</td>\n",
       "      <td>2.0</td>\n",
       "      <td>12.5</td>\n",
       "      <td>2178.2</td>\n",
       "      <td>422.3</td>\n",
       "      <td>1160.0</td>\n",
       "      <td>385.4</td>\n",
       "      <td>1.9</td>\n",
       "      <td>1.2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>32.5</td>\n",
       "      <td>-124.3</td>\n",
       "      <td>1.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.5</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>33.9</td>\n",
       "      <td>-121.8</td>\n",
       "      <td>18.0</td>\n",
       "      <td>1455.8</td>\n",
       "      <td>295.0</td>\n",
       "      <td>784.0</td>\n",
       "      <td>279.0</td>\n",
       "      <td>2.6</td>\n",
       "      <td>1.5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>34.3</td>\n",
       "      <td>-118.5</td>\n",
       "      <td>29.0</td>\n",
       "      <td>2122.0</td>\n",
       "      <td>433.0</td>\n",
       "      <td>1165.0</td>\n",
       "      <td>409.0</td>\n",
       "      <td>3.5</td>\n",
       "      <td>1.9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>37.7</td>\n",
       "      <td>-118.0</td>\n",
       "      <td>37.0</td>\n",
       "      <td>3155.2</td>\n",
       "      <td>650.0</td>\n",
       "      <td>1713.0</td>\n",
       "      <td>606.0</td>\n",
       "      <td>4.8</td>\n",
       "      <td>2.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>42.0</td>\n",
       "      <td>-114.3</td>\n",
       "      <td>52.0</td>\n",
       "      <td>37937.0</td>\n",
       "      <td>6445.0</td>\n",
       "      <td>35682.0</td>\n",
       "      <td>6082.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>55.2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       latitude  longitude  housing_median_age  total_rooms  total_bedrooms  \\\n",
       "count   12000.0    12000.0             12000.0      12000.0         12000.0   \n",
       "mean       35.6     -119.6                28.6       2634.1           537.9   \n",
       "std         2.1        2.0                12.5       2178.2           422.3   \n",
       "min        32.5     -124.3                 1.0          8.0             1.0   \n",
       "25%        33.9     -121.8                18.0       1455.8           295.0   \n",
       "50%        34.3     -118.5                29.0       2122.0           433.0   \n",
       "75%        37.7     -118.0                37.0       3155.2           650.0   \n",
       "max        42.0     -114.3                52.0      37937.0          6445.0   \n",
       "\n",
       "       population  households  median_income  rooms_per_person  \n",
       "count     12000.0     12000.0        12000.0           12000.0  \n",
       "mean       1422.1       499.5            3.9               2.0  \n",
       "std        1160.0       385.4            1.9               1.2  \n",
       "min           8.0         1.0            0.5               0.0  \n",
       "25%         784.0       279.0            2.6               1.5  \n",
       "50%        1165.0       409.0            3.5               1.9  \n",
       "75%        1713.0       606.0            4.8               2.3  \n",
       "max       35682.0      6082.0           15.0              55.2  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation examples summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>latitude</th>\n",
       "      <th>longitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>rooms_per_person</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>35.6</td>\n",
       "      <td>-119.5</td>\n",
       "      <td>28.5</td>\n",
       "      <td>2666.7</td>\n",
       "      <td>542.9</td>\n",
       "      <td>1447.4</td>\n",
       "      <td>505.3</td>\n",
       "      <td>3.9</td>\n",
       "      <td>2.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>2.1</td>\n",
       "      <td>2.0</td>\n",
       "      <td>12.7</td>\n",
       "      <td>2184.2</td>\n",
       "      <td>419.6</td>\n",
       "      <td>1118.2</td>\n",
       "      <td>382.5</td>\n",
       "      <td>1.9</td>\n",
       "      <td>1.1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>32.5</td>\n",
       "      <td>-124.3</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>0.5</td>\n",
       "      <td>0.1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>33.9</td>\n",
       "      <td>-121.7</td>\n",
       "      <td>18.0</td>\n",
       "      <td>1474.8</td>\n",
       "      <td>300.0</td>\n",
       "      <td>801.0</td>\n",
       "      <td>285.0</td>\n",
       "      <td>2.6</td>\n",
       "      <td>1.5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>34.2</td>\n",
       "      <td>-118.5</td>\n",
       "      <td>29.0</td>\n",
       "      <td>2146.0</td>\n",
       "      <td>435.0</td>\n",
       "      <td>1171.0</td>\n",
       "      <td>410.0</td>\n",
       "      <td>3.6</td>\n",
       "      <td>1.9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>37.7</td>\n",
       "      <td>-118.0</td>\n",
       "      <td>37.0</td>\n",
       "      <td>3142.0</td>\n",
       "      <td>645.0</td>\n",
       "      <td>1751.2</td>\n",
       "      <td>605.0</td>\n",
       "      <td>4.8</td>\n",
       "      <td>2.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>41.8</td>\n",
       "      <td>-114.6</td>\n",
       "      <td>52.0</td>\n",
       "      <td>30401.0</td>\n",
       "      <td>4957.0</td>\n",
       "      <td>13251.0</td>\n",
       "      <td>4372.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>41.3</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       latitude  longitude  housing_median_age  total_rooms  total_bedrooms  \\\n",
       "count    5000.0     5000.0              5000.0       5000.0          5000.0   \n",
       "mean       35.6     -119.5                28.5       2666.7           542.9   \n",
       "std         2.1        2.0                12.7       2184.2           419.6   \n",
       "min        32.5     -124.3                 2.0          2.0             2.0   \n",
       "25%        33.9     -121.7                18.0       1474.8           300.0   \n",
       "50%        34.2     -118.5                29.0       2146.0           435.0   \n",
       "75%        37.7     -118.0                37.0       3142.0           645.0   \n",
       "max        41.8     -114.6                52.0      30401.0          4957.0   \n",
       "\n",
       "       population  households  median_income  rooms_per_person  \n",
       "count      5000.0      5000.0         5000.0            5000.0  \n",
       "mean       1447.4       505.3            3.9               2.0  \n",
       "std        1118.2       382.5            1.9               1.1  \n",
       "min           3.0         2.0            0.5               0.1  \n",
       "25%         801.0       285.0            2.6               1.5  \n",
       "50%        1171.0       410.0            3.6               1.9  \n",
       "75%        1751.2       605.0            4.8               2.3  \n",
       "max       13251.0      4372.0           15.0              41.3  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training targets summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>median_house_value</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>12000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>207.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>116.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>119.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>180.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>263.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>500.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       median_house_value\n",
       "count             12000.0\n",
       "mean                207.0\n",
       "std                 116.0\n",
       "min                  15.0\n",
       "25%                 119.3\n",
       "50%                 180.3\n",
       "75%                 263.8\n",
       "max                 500.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation targets summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>median_house_value</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>5000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>208.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>115.9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>120.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>180.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>268.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>500.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       median_house_value\n",
       "count              5000.0\n",
       "mean                208.0\n",
       "std                 115.9\n",
       "min                  15.0\n",
       "25%                 120.0\n",
       "50%                 180.4\n",
       "75%                 268.0\n",
       "max                 500.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Choose the first 12000 (out of 17000) examples for training.\n",
    "training_examples = preprocess_features(california_housing_dataframe.head(12000))\n",
    "training_targets = preprocess_targets(california_housing_dataframe.head(12000))\n",
    "\n",
    "# Choose the last 5000 (out of 17000) examples for validation.\n",
    "validation_examples = preprocess_features(california_housing_dataframe.tail(5000))\n",
    "validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))\n",
    "\n",
    "# Double-check that we've done the right thing.\n",
    "print(\"Training examples summary:\")\n",
    "display.display(training_examples.describe())\n",
    "print(\"Validation examples summary:\")\n",
    "display.display(validation_examples.describe())\n",
    "\n",
    "print(\"Training targets summary:\")\n",
    "display.display(training_targets.describe())\n",
    "print(\"Validation targets summary:\")\n",
    "display.display(validation_targets.describe())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def construct_feature_columns(input_features):\n",
    "  \"\"\"Construct the TensorFlow Feature Columns.\n",
    "\n",
    "  Args:\n",
    "    input_features: The names of the numerical input features to use.\n",
    "  Returns:\n",
    "    A set of feature columns\n",
    "  \"\"\" \n",
    "  return set([tf.feature_column.numeric_column(my_feature)\n",
    "              for my_feature in input_features])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):\n",
    "    \"\"\"Trains a linear regression model of one feature.\n",
    "  \n",
    "    Args:\n",
    "      features: pandas DataFrame of features\n",
    "      targets: pandas DataFrame of targets\n",
    "      batch_size: Size of batches to be passed to the model\n",
    "      shuffle: True or False. Whether to shuffle the data.\n",
    "      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely\n",
    "    Returns:\n",
    "      Tuple of (features, labels) for next data batch\n",
    "    \"\"\"\n",
    "    \n",
    "    # Convert pandas data into a dict of np arrays.\n",
    "    features = {key:np.array(value) for key,value in dict(features).items()}                                             \n",
    " \n",
    "    # Construct a dataset, and configure batching/repeating\n",
    "    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit\n",
    "    ds = ds.batch(batch_size).repeat(num_epochs)\n",
    "    \n",
    "    # Shuffle the data, if specified\n",
    "    if shuffle:\n",
    "      ds = ds.shuffle(10000)\n",
    "    \n",
    "    # Return the next batch of data\n",
    "    features, labels = ds.make_one_shot_iterator().get_next()\n",
    "    return features, labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_nn_regression_model(\n",
    "    learning_rate,\n",
    "    steps,\n",
    "    batch_size,\n",
    "    hidden_units,\n",
    "    training_examples,\n",
    "    training_targets,\n",
    "    validation_examples,\n",
    "    validation_targets):\n",
    "  \"\"\"Trains a neural network regression model.\n",
    "  \n",
    "  In addition to training, this function also prints training progress information,\n",
    "  as well as a plot of the training and validation loss over time.\n",
    "  \n",
    "  Args:\n",
    "    learning_rate: A `float`, the learning rate.\n",
    "    steps: A non-zero `int`, the total number of training steps. A training step\n",
    "      consists of a forward and backward pass using a single batch.\n",
    "    batch_size: A non-zero `int`, the batch size.\n",
    "    hidden_units: A `list` of int values, specifying the number of neurons in each layer.\n",
    "    training_examples: A `DataFrame` containing one or more columns from\n",
    "      `california_housing_dataframe` to use as input features for training.\n",
    "    training_targets: A `DataFrame` containing exactly one column from\n",
    "      `california_housing_dataframe` to use as target for training.\n",
    "    validation_examples: A `DataFrame` containing one or more columns from\n",
    "      `california_housing_dataframe` to use as input features for validation.\n",
    "    validation_targets: A `DataFrame` containing exactly one column from\n",
    "      `california_housing_dataframe` to use as target for validation.\n",
    "      \n",
    "  Returns:\n",
    "    A `LinearRegressor` object trained on the training data.\n",
    "  \"\"\"\n",
    "\n",
    "  periods = 10\n",
    "  steps_per_period = steps / periods\n",
    "  \n",
    "  # Create a linear regressor object.\n",
    "  my_optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n",
    "  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
    "  dnn_regressor = tf.estimator.DNNRegressor(\n",
    "      feature_columns=construct_feature_columns(training_examples),\n",
    "      hidden_units=hidden_units\n",
    "  )\n",
    "  \n",
    "  # Create input functions\n",
    "  training_input_fn = lambda: my_input_fn(training_examples, \n",
    "                                          training_targets[\"median_house_value\"], \n",
    "                                          batch_size=batch_size)\n",
    "  predict_training_input_fn = lambda: my_input_fn(training_examples, \n",
    "                                                  training_targets[\"median_house_value\"], \n",
    "                                                  num_epochs=1, \n",
    "                                                  shuffle=False)\n",
    "  predict_validation_input_fn = lambda: my_input_fn(validation_examples, \n",
    "                                                    validation_targets[\"median_house_value\"], \n",
    "                                                    num_epochs=1, \n",
    "                                                    shuffle=False)\n",
    "\n",
    "  # Train the model, but do so inside a loop so that we can periodically assess\n",
    "  # loss metrics.\n",
    "  print(\"Training model...\")\n",
    "  print(\"RMSE (on training data):\")\n",
    "  training_rmse = []\n",
    "  validation_rmse = []\n",
    "  for period in range (0, periods):\n",
    "    # Train the model, starting from the prior state.\n",
    "    dnn_regressor.train(\n",
    "        input_fn=training_input_fn,\n",
    "        steps=steps_per_period\n",
    "    )\n",
    "    # Take a break and compute predictions.\n",
    "    training_predictions = dnn_regressor.predict(input_fn=predict_training_input_fn)\n",
    "    training_predictions = np.array([item['predictions'][0] for item in training_predictions])\n",
    "    \n",
    "    validation_predictions = dnn_regressor.predict(input_fn=predict_validation_input_fn)\n",
    "    validation_predictions = np.array([item['predictions'][0] for item in validation_predictions])\n",
    "    \n",
    "    # Compute training and validation loss.\n",
    "    training_root_mean_squared_error = math.sqrt(\n",
    "        metrics.mean_squared_error(training_predictions, training_targets))\n",
    "    validation_root_mean_squared_error = math.sqrt(\n",
    "        metrics.mean_squared_error(validation_predictions, validation_targets))\n",
    "    # Occasionally print the current loss.\n",
    "    print(\"  period %02d : %0.2f\" % (period, training_root_mean_squared_error))\n",
    "    # Add the loss metrics from this period to our list.\n",
    "    training_rmse.append(training_root_mean_squared_error)\n",
    "    validation_rmse.append(validation_root_mean_squared_error)\n",
    "  print(\"Model training finished.\")\n",
    "\n",
    "  # Output a graph of loss metrics over periods.\n",
    "  plt.ylabel(\"RMSE\")\n",
    "  plt.xlabel(\"Periods\")\n",
    "  plt.title(\"Root Mean Squared Error vs. Periods\")\n",
    "  plt.tight_layout()\n",
    "  plt.plot(training_rmse, label=\"training\")\n",
    "  plt.plot(validation_rmse, label=\"validation\")\n",
    "  plt.legend()\n",
    "\n",
    "  print(\"Final RMSE (on training data):   %0.2f\" % training_root_mean_squared_error)\n",
    "  print(\"Final RMSE (on validation data): %0.2f\" % validation_root_mean_squared_error)\n",
    "\n",
    "  return dnn_regressor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assignment 1:训练神经网络模型\n",
    "调整超参数，目标是将 RMSE 降到 110 以下\n",
    "> 神经网络由 DNNRegressor 类定义。\n",
    "  使用 hidden_units 定义神经网络的结构。hidden_units 参数会创建一个整数列表，其中每个整数对应一个隐藏层，表示其中的节点数。以下面的赋值为例：\n",
    "hidden_units=[3,10]\n",
    "上述赋值为神经网络指定了两个隐藏层：\n",
    "第一个隐藏层包含 3 个节点。\n",
    "第二个隐藏层包含 10 个节点。\n",
    "如果我们想要添加更多层，可以向该列表添加更多整数。例如，hidden_units=[10,20,30,40] 会创建 4 个分别包含 10、20、30 和 40 个单元的隐藏层。\n",
    "默认情况下，所有隐藏层都会使用 ReLu 激活函数，且是全连接层。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training model...\n",
      "RMSE (on training data):\n",
      "  period 00 : 179.59\n",
      "  period 01 : 136.35\n",
      "  period 02 : 121.62\n",
      "  period 03 : 107.40\n",
      "  period 04 : 104.05\n",
      "  period 05 : 102.13\n",
      "  period 06 : 102.61\n",
      "  period 07 : 100.78\n",
      "  period 08 : 101.72\n",
      "  period 09 : 101.07\n",
      "Model training finished.\n",
      "Final RMSE (on training data):   101.07\n",
      "Final RMSE (on validation data): 100.13\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "dnn_regressor = train_nn_regression_model(\n",
    "    learning_rate=0.01,\n",
    "    steps=500,\n",
    "    batch_size=10,\n",
    "    hidden_units=[10, 10, 10],\n",
    "    training_examples=training_examples,\n",
    "    training_targets=training_targets,\n",
    "    validation_examples=validation_examples,\n",
    "    validation_targets=validation_targets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assignment 2: 用测试数据进行评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final RMSE (on test data): 98.98\n"
     ]
    }
   ],
   "source": [
    "california_housing_test_data = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_test.csv\", sep=\",\")\n",
    "\n",
    "test_examples = preprocess_features(california_housing_test_data)\n",
    "test_targets = preprocess_targets(california_housing_test_data)\n",
    "\n",
    "predict_testing_input_fn = lambda: my_input_fn(test_examples, \n",
    "                                               test_targets[\"median_house_value\"], \n",
    "                                               num_epochs=1, \n",
    "                                               shuffle=False)\n",
    "\n",
    "test_predictions = dnn_regressor.predict(input_fn=predict_testing_input_fn)\n",
    "test_predictions = np.array([item['predictions'][0] for item in test_predictions])\n",
    "\n",
    "root_mean_squared_error = math.sqrt(metrics.mean_squared_error(test_predictions, test_targets))\n",
    "\n",
    "print(\"Final RMSE (on test data): %0.2f\" % root_mean_squared_error)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
